<?php


class BilibiliClient extends \GuzzleHttp\Client
{

    protected $defaultConfigs = [
        'allow_redirects' => true,
        'read_timeout' => 10,
        'connect_timeout' => 10,
        'verify' => false
    ];

    public function __construct(array $config = [])
    {

        parent::__construct(array_merge($this->defaultConfigs,$config));
    }

    /**
     * 获取直播间信息
     * @param $roomId
     * @return mixed
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function room_v1_room_get_info($roomId){

        return json_decode($this->get('https://api.live.bilibili.com/room/v1/Room/get_info', [
            'headers' => [
                'Accept'=> 'application/json, text/plain, */*',
                'Accept-Encoding'=> 'gzip, deflate, br',
                'Accept-Language'=> 'zh-CN,zh;q=0.9',
                'Host'=> 'api.live.bilibili.com',
                'Origin'=> 'https://live.bilibili.com',
                'Referer'=> 'https://live.bilibili.com/' . $roomId,
                'Connection'=> 'keep-alive',
                'User-Agent'=> 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36'
            ],
            'query' =>[
                'id' => $roomId
            ]
        ])->getBody()->getContents(),JSON_OBJECT_AS_ARRAY);
    }

    /**
     * 获取直播地址
     * @param $roomId
     * @param $quality
     * @param string $platform
     * @return mixed
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function room_v1_room_playUrl($roomId,$quality,$platform = 'web'){

        return json_decode($this->get('https://api.live.bilibili.com/room/v1/Room/playUrl', [
            'headers' => [
                'Accept'=> 'application/json, text/plain, */*',
                'Accept-Encoding'=> 'gzip, deflate, br',
                'Accept-Language'=> 'zh-CN,zh;q=0.9',
                'Host'=> 'api.live.bilibili.com',
                'Origin'=> 'https://live.bilibili.com',
                'Referer'=> 'https://live.bilibili.com/' . $roomId,
                'Connection'=> 'keep-alive',
                'User-Agent'=> 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36'
            ],
            'query' =>[
                'cid' => $roomId,
                'quality' => $quality,
                'platform' =>$platform
            ]
        ])->getBody()->getContents(),JSON_OBJECT_AS_ARRAY);
    }

    /**
     * 下载直播流
     * @param $liveUrl
     * @param $savedFile
     * @param float|int $buf
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function getFLVStream($liveUrl){
        return $this->get($liveUrl, [
            'headers' =>[
                'Accept' => '*/*',
                'Accept-Encoding'=>'gzip, deflate',
                'Host' => parse_url($liveUrl, PHP_URL_HOST),
                'Origin' => 'https://live.bilibili.com',
                'Reference' => 'https://live.bilibili.com',
                'User-Agent'=>'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36'
            ],
            'stream' => true,
        ]);
    }




    public function getM3U8IndexV2($url,$savedFile,$roomId){
        $hasDownloadFirst = false;
        $urlInfo = parse_url($url);

        parse_str($urlInfo['query'],$urlInfo['query']);

        $basePath = mb_substr($url,0,mb_strpos($url,'index.m3u8') );
        $headers = [
            'Accept' => '*/*',
            'Accept-Encoding' => 'gzip, deflate',
            'Host' => $urlInfo['host'],
            'Origin' => 'https://live.bilibili.com',
            'Reference' => 'https://live.bilibili.com',
            'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36'
        ];
        $parser = new \Chrisyue\PhpM3u8\Facade\ParserFacade();

        $fp = fopen($savedFile,'wb+');
        $sequence = 0;
        while(true){
            $raw = '';
            try {
                $index  = $this->get($url, [
                    'headers' =>$headers,
                ])->getBody()->getContents();
            } catch (\GuzzleHttp\Exception\RequestException $throwable) {

                echo 'Code:'.$throwable->getCode().PHP_EOL;

                $t = $this->room_v1_room_get_info($roomId);

                if (intval($t['data']['live_status']) !== 1) {
                    fclose($fp);
                    throw new \Exception("$roomId 主播未开播");
                }
                continue;
            }


            $mediaPlaylist = $parser->parse(new \Chrisyue\PhpM3u8\Stream\TextStream($index));

            if (empty($mediaPlaylist['mediaSegments'])) {
                //可能结束了直播
                print_r($index);
                break;
            }
            $firstSegment = $mediaPlaylist['mediaSegments'][0];
            if (!$hasDownloadFirst) {
                $raw .= $this->get($basePath . $firstSegment['EXT-X-MAP']['URI'], [
                    'headers' => $headers,
                    'query' => [
                        'trid' => $urlInfo['query']['trid']
                    ],
                ])->getBody()->getContents();
                $hasDownloadFirst = true;
            }
            $currentSequence = intval($mediaPlaylist['EXT-X-MEDIA-SEQUENCE']);

            $promises = [];

            foreach ($mediaPlaylist['mediaSegments'] as $item) {

                if ($currentSequence > $sequence) {
                    $promises[$item['uri']] = $this->getAsync($basePath . $item['uri'],[
                        'headers' => $headers,
                        'query' => [
                            'trid' => $urlInfo['query']['trid']
                        ]
                    ]);

                }
                $currentSequence++;
            }
            $responses = \GuzzleHttp\Promise\Utils::settle($promises)->wait();
            foreach ($responses as $k=> $response) {
                if ($response['state'] === 'fulfilled') {
                    $raw .= $response['value']->getBody()->getContents();
                   // echo   $k . PHP_EOL;
                }else{
                    echo  'rejected  '. $k . PHP_EOL;
                }
            }
            if ($currentSequence > $sequence) {
                $sequence = $currentSequence-1;
            }
            if ($raw !== '') {
                fwrite($fp, $raw);
            }
        }
        fclose($fp);
    }

    /**
     * @param $roomId
     * @param int $qn
     * @return mixed
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function xlive_web_room_v2_index_getRoomPlayInfo($roomId,$qn = 10000){
        //qn: 10000原画  400蓝光 250超清 150高清 80流畅

        return json_decode($this->get('https://api.live.bilibili.com/xlive/web-room/v2/index/getRoomPlayInfo',[
            'headers' => [
                'Host' => 'api.live.bilibili.com',
                'Origin' => 'https://live.bilibili.com',
                'Reference' => 'https://live.bilibili.com',
                'User-Agent'=>'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36'
            ],
            'query' => [
                'room_id' => $roomId,
                'protocol' => '0,1',
                'format' => '0,2',
                'codec' => '0,1',
                'qn' => $qn,
                'platform' => 'web',
                'ptype' => 16
            ]
        ])->getBody()->getContents(), JSON_OBJECT_AS_ARRAY);
    }


    /**
     * BB空间动态
     * @param array $params
     * @return mixed
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function dynamic_svr_space_history($params = []){

        $params = array_merge($default = [
            'offset_dynamic_id' => 0 ,
            'host_uid' => '' ,
            'visitor_uid' => 0  ,
            'need_top' => 1  ,
            'platform' =>'web'
        ],$params);
        return json_decode($this->get('https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/space_history',[
            'headers' => [
                'Referer' => 'https://space.bilibili.com/',
                'Origin' => 'https://space.bilibili.com',
                'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36'
            ],
            'query' => $params
        ])->getBody()->getContents(),JSON_OBJECT_AS_ARRAY);


    }

    /**
     * 保存直播流
     * @param $body
     * @param $savedFile
     * @param float|int $buf
     */
    public function storeStreamData($body,$savedFile,$buf = 1024*1024*20){
        $f = fopen($savedFile, 'wb+');
        while (!$body->eof()) {
            fwrite($f,$body->read($buf));
        }
        fclose($f);
    }
}
